package com.xzzz.common.base.util;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import com.xzzz.common.base.exception.XzException500;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.cglib.beans.BeanCopier;

import java.util.ArrayList;
import java.util.List;

/**
 * @author xzzz
 * 
 */
@SuppressWarnings("all")
public class BeanUtil extends org.springframework.beans.BeanUtils {

    private static final Logger log = LoggerFactory.getLogger(BeanUtil.class);

    /**
     * 对象拷贝 (数据源为空返回空)
     *
     * @param from 数据源
     * @param to   目标对象
     * @param <T>  范型
     * @return 数据源为空返回空
     */
    public static <T> T toObj(Object from, Class to) throws BeansException {
        if (null == from) {
            return null;
        }
        Object dest;
        try {
            dest = to.newInstance();
            copyProperties(from, dest);
        } catch (Exception e) {
            throw new XzException500("BeanUtil.copyObject 转换异常: " + e.getMessage());
        }
        return (T) dest;
    }

    /**
     * Spirng 原生的 bean copy, 比{@link BeanUtil#toObj(Object, Class)} 快, 但麻烦的是需要在外部创建对象
     *
     * @param from 源文件的, 需要先创建对象
     * @param to   目标文件, 需要先创建对象
     */
    public static void toObj(Object from, Object to) throws BeansException {
        if (null == from || to == null) {
            return;
        }
        try {
            copyProperties(from, to);
        } catch (Exception e) {
            throw new XzException500("BeanUtil.copyObject 转换异常: " + e.getMessage());
        }
    }

    /**
     * BeanCopier的copy, 父类字段无法复制, 但是速度最快
     *
     * @param from 源文件的, 需要先创建对象
     * @param to   目标文件, 需要先创建对象
     */
    public static void toObjByCopier(Object from, Object to) {
        BeanCopier beanCopier = BeanCopier.create(from.getClass(), to.getClass(), false);
        beanCopier.copy(from, to, null);
    }

    /**
     * 拷贝集合
     *
     * @param from 数据源
     * @param to   目标
     * @param <T>  范型
     * @return 数据源为空返回空
     */
    public static <T> List<T> toList(List from, Class to) {
        if (null == from || from.isEmpty()) {
            return null;
        }

        List<T> dest = new ArrayList<>();
        from.forEach((Object fromItem) -> {
            try {
                T toObject = (T) to.newInstance();
                copyProperties(fromItem, toObject);
                dest.add(toObject);
            } catch (Exception e) {
                throw new XzException500("BeanUtil.copyObject 转换异常: " + e.getMessage());
            }
        });
        return dest;
    }


    // region ------------------------------------------<Benchmark>------------------------------------------

    private static final int warmUpNum = 5_000_000;
    private static final int copyNum = 1000;

    /**
     * 几种复制对象的基准测试
     */
    private static void benchmark() {
        warmUp();
        System.gc();
        toObjByCopierBenchmark();
        System.gc();
        toObjBenchmark();
        System.gc();
        copyPropertiesBenchmark();
    }

    private static void warmUp() {
        long warmUpStart = System.currentTimeMillis();
        System.out.println("==================== 基准测试: JVM预热");
        for (int i = 0; i < warmUpNum; i++) {
            Benchmark1.init();
        }
        System.out.println(String.format("JVM预热用时: %s ms", (System.currentTimeMillis() - warmUpStart)));
    }

    /**
     * toObj 的基准测试
     */
    private static void toObjBenchmark() {
        System.out.println("==================== 基准测试: toObjBenchmark");
        long genStart = System.currentTimeMillis();
        List<Benchmark1> from = new ArrayList<>();
        for (int i = 0; i < copyNum; i++) {
            from.add(Benchmark1.init());
        }
        System.out.println(String.format("生成数据用时: %s ms", (System.currentTimeMillis() - genStart)));

        long copyStart = System.currentTimeMillis();
        List<Benchmark2> to = toList(from, Benchmark2.class);
        System.out.println(String.format("复制数据用时: %s ms", (System.currentTimeMillis() - copyStart)));
    }

    /**
     * Spring 自带的复制方法基准测试
     */
    private static void copyPropertiesBenchmark() {
        System.out.println("==================== 基准测试: copyPropertiesBenchmark");
        long genStart = System.currentTimeMillis();
        List<Benchmark1> from = new ArrayList<>();
        for (int i = 0; i < copyNum; i++) {
            from.add(Benchmark1.init());
        }
        System.out.println(String.format("生成数据用时: %s ms", (System.currentTimeMillis() - genStart)));

        long copyStart = System.currentTimeMillis();
        List<Benchmark2> to = new ArrayList<>();
        for (Benchmark1 b1 : from) {
            Benchmark2 b2 = new Benchmark2();
            copyProperties(b1, b2);
            to.add(b2);
        }
        System.out.println(String.format("复制数据用时: %s ms", (System.currentTimeMillis() - copyStart)));
    }

    /**
     * toObj 的基准测试
     */
    private static void toObjByCopierBenchmark() {
        System.out.println("==================== 基准测试: toObjByCopierBenchmark");
        long genStart = System.currentTimeMillis();
        List<Benchmark1> from = new ArrayList<>();
        for (int i = 0; i < 500_000; i++) {
            from.add(Benchmark1.init());
        }
        System.out.println(String.format("生成数据用时: %s ms", (System.currentTimeMillis() - genStart)));

        long copyStart = System.currentTimeMillis();
        List<Benchmark2> to = new ArrayList<>();
        for (Benchmark1 b1 : from) {
            Benchmark2 b2 = new Benchmark2();
            toObjByCopier(b1, b2);
            to.add(b2);
        }
        System.out.println(String.format("复制数据用时: %s ms", (System.currentTimeMillis() - copyStart)));
    }


    @Data
    private static class Benchmark1 {
        private String str1;
        private String str2;
        private String str3;
        private Long long1;
        private Long long2;
        private Long long3;
        private Integer int1;
        private Integer int2;
        private Integer int3;
        private Boolean bool1;
        private Boolean bool2;
        private Boolean bool3;

        public static Benchmark1 init() {
            Benchmark1 benchmark1 = new Benchmark1();
            benchmark1.setStr1(UUID.fastUUID().toString(true));
            benchmark1.setStr2(UUID.fastUUID().toString(true));
            benchmark1.setStr3(UUID.fastUUID().toString(true));
            benchmark1.setLong1(RandomUtil.randomLong());
            benchmark1.setLong2(RandomUtil.randomLong());
            benchmark1.setLong3(RandomUtil.randomLong());
            benchmark1.setInt1(RandomUtil.randomInt());
            benchmark1.setInt2(RandomUtil.randomInt());
            benchmark1.setInt3(RandomUtil.randomInt());
            benchmark1.setBool1(RandomUtil.randomBoolean());
            benchmark1.setBool2(RandomUtil.randomBoolean());
            benchmark1.setBool3(RandomUtil.randomBoolean());
            return benchmark1;
        }
    }

    @Data
    private static class Benchmark2 extends Benchmark1 {
    }

    // endregion


    public static void main(String[] args) {
        benchmark();
    }

}